package pl.edu.pw.stud.ltrzaska.logic;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.log4j.Logger;

import pl.edu.pw.stud.ltrzaska.policy.entity.Policy;
import pl.edu.pw.stud.ltrzaska.policy.entity.PolicyValidator;
import pl.edu.pw.stud.ltrzaska.validator.ContentValidator;
import pl.edu.pw.stud.ltrzaska.validator.ExistenceValidator;
import pl.edu.pw.stud.ltrzaska.validator.NameValidator;
import pl.edu.pw.stud.ltrzaska.validator.SizeValidator;
import pl.edu.pw.stud.ltrzaska.validator.SystemPropertyValidator;
import pl.edu.pw.stud.ltrzaska.validator.TimeValidator;
import pl.edu.pw.stud.ltrzaska.validator.iface.IFilesystemObjectValidator;

/**
 * Kontekst walidacji. Zawiera wszystkie potrzebne informacje do przeprowadzenia
 * walidacji polityki poprawnosci.
 * 
 * @author luk
 * 
 */
public class ValidationContext {
	private static final Logger LOGGER = Logger.getLogger(ValidationContext.class.getName());
	private List<IValidationObserver> violationObservers;
	/**
	 * Analizowana polityka poprawnosci
	 */
	Policy policy;
	/**
	 * Punkt wejsciowy do wirtualnego systemu plikow
	 */
	FileObject virtualFsEntry;

	/**
	 * Lista odnalezionych niezgodnosci miedzy wirtualnym systemem plikow a
	 * polityka poprawnosci
	 */
	List<PolicyViolation> violation = new ArrayList<PolicyViolation>();

	public ValidationContext(Policy policy, FileObject virtualFsEntry) {
		super();
		this.policy = policy;
		this.virtualFsEntry = virtualFsEntry;
		this.violationObservers = new LinkedList<IValidationObserver>();
		
		LOGGER.debug("ValidationContext created.");
	}
	/**
	 * Dodaje obserwatora procesu walidacji ktory bedzie informowany o znalezionych niezgodnosciach
	 * @param ivo
	 */
	public void registerObserver(IValidationObserver ivo) {
		this.violationObservers.add(ivo);
	}
	/**
	 * Informuje obserwatorow o znalezionych niezgodnosciach
	 * @param ivo
	 */
	public void notifyObservers(PolicyViolation pv) {
		for(IValidationObserver ivo : this.violationObservers) {
			ivo.notify(pv);
		}
	}
	/**
	 * Informuje obserwatorow o znalezionych niezgodnosciach
	 * @param ivo
	 */
	public void notifyObservers(String msg) {
		for(IValidationObserver ivo : this.violationObservers) {
			ivo.notify(msg);
		}
	}
	
	/**
	 * Zwraca wszystkie znalezione niezgodnosci.
	 * 
	 * @return
	 */
	public List<PolicyViolation> getViolations() {
		return violation;
	}

	/**
	 * Dodaj niezgodnosc do znalezionych.
	 * 
	 * @param viol
	 */
	public void addViolation(PolicyViolation viol) {
		this.violation.add(viol);
		this.notifyObservers(viol);
	}

	public void setVirtualFsEntry(FileObject f) {
		this.virtualFsEntry = f;
	}

	public void setPolicy(Policy p) {
		this.policy = p;
	}

	/**
	 * Sprawdza poprawnosc plikow i katalogow z wirtualnego systemu plikow za
	 * pomoca walidatorow. Najepierw standardowych, pozniej ewentualnych
	 * dodatkowych.
	 * 
	 * @param equivalents
	 * @throws FileSystemException
	 */
	public void run(Map<String, LinkedList<FsObjectEquivalent>> equivalents) throws FileSystemException {
		Set<Entry<String, LinkedList<FsObjectEquivalent>>> eqsSet = equivalents.entrySet();

		Map<String, IFilesystemObjectValidator> valids = createValidatorsInstances();

		// wlasciwe dzialanie aplikacji

		for (Map.Entry<String, LinkedList<FsObjectEquivalent>> entry : eqsSet) {
			for (FsObjectEquivalent eq : entry.getValue()) {
				List<PolicyValidator> pvs = eq.getPolicyFsObject().getAdditionalValidators();
				if (pvs != null) {
					for (PolicyValidator pv : pvs) {
						// TODO Dynamiczne doladowanie klas dodatkowych
						// walidatorow
					}
				}
				// walidacja wszystkimi walidatorami
				for (Map.Entry<String, IFilesystemObjectValidator> ent : valids.entrySet()) {
					IFilesystemObjectValidator valid = ent.getValue();
					if (valid.canValidate(eq.getPolicyFsObject(), eq.getVirtualFsObject())) {
						valid.validate(eq.getPolicyFsObject(), eq.getVirtualFsObject(), this);
					}
				}
			}
		}
		
		notifyObservers("Zakocznono sprawdzanie poprawnosci. Wykryto niezgodnosci: " + this.violation.size());
	}
	/**
	 * Tworzy instancje walidatorow standardowych
	 * 
	 * @return
	 */
	private Map<String, IFilesystemObjectValidator> createValidatorsInstances() {
		Map<String, IFilesystemObjectValidator> valids = new HashMap<String, IFilesystemObjectValidator>();
		valids.put("existence", new ExistenceValidator());
		valids.put("content", new ContentValidator());
		valids.put("name", new NameValidator());
		valids.put("size", new SizeValidator());
		valids.put("systemProperty", new SystemPropertyValidator());
		valids.put("timeValidator", new TimeValidator());
		return valids;
	}

	/**
	 * Zaraportuj niezgodnosc. Metoda do uzywania przez walidatory w momencie
	 * znalezienia niezgodnosci.
	 * 
	 * @param disc
	 */
	public void reportDiscrepancy(PolicyViolation disc) {
		this.addViolation(disc);
	}
}
